#include "Empleado.h"

Empleado::Empleado(int id, const string& nombreProceso, Surtidores& surtidores,
		Semaforos& semaforos, bool logActivo, int nEmpleado, int cantEmpleados) :
		Proceso(nombreProceso, logActivo), semaforoUtilizandoCaja(
				Constantes::ARCHIVO_SEMAFORO_CAJA, 1, 0), semaforoPeticionCaja(
				Constantes::ARCHIVO_CLAVE_SEMAFORO_ESTADO_EMPLEADO, nEmpleado,
				0), prioridadCaja(2), colaMensajesCaja(
				Constantes::ARCHIVO_COLA_MENSAJES_CAJA, 'a'), id_(id), surtidores_(
				surtidores), semaforos_(semaforos), memoria_clientes_(
				Constantes::ARCHIVO_CLAVE_JEFE_EMPLEADO, nEmpleado), empleados_ocupados_(
				Constantes::ARCHIVO_CLAVE_ESTADO_JEFE_EMPLEADO, nEmpleado), acceder_memoria_clientes_(
				Constantes::ARCHIVO_CLAVE_SEMAFORO_JEFE_EMPLEADO,
				nEmpleado + cantEmpleados), estado_empleado_(
				Constantes::ARCHIVO_CLAVE_SEMAFORO_ESTADO_EMPLEADO, nEmpleado), numero_empleado_(
				nEmpleado) {
	this->empleados_ocupados_.escribir(false);
}

Empleado::~Empleado() {
}

void Empleado::eliminar() {
	memoria_clientes_.liberar();
	empleados_ocupados_.liberar();
	acceder_memoria_clientes_.eliminar();
	estado_empleado_.eliminar();
	semaforoPeticionCaja.eliminar();
}

// AGREGAR COPIA DE MEMORIAS COMPARTIDAS Y SEMAFOROS
Empleado::Empleado(const Empleado& otro) :
		Proceso(otro), prioridadCaja(otro.prioridadCaja), colaMensajesCaja(
				otro.colaMensajesCaja), id_(otro.id_) {
	setPID(otro.pid);
}
Empleado& Empleado::operator=(const Empleado& otro)
{
//	prioridadCaja = otro.prioridadCaja;
	colaMensajesCaja  = otro.colaMensajesCaja;
	id_=otro.id_;
	setPID(otro.pid);
	return (*this);
}
int Empleado::getId() {
	return id_;
}

void Empleado::cobrar_y_guardar_en_caja(Cliente& c) {
	MensajePeticionCaja mpc;
	mpc.idDemandanteDeCaja = numero_empleado_+1; // TODO ver si hace falta el +1
	mpc.mtype = prioridadCaja;
	// TODO loggear
	// Dinero que se le cobra al cliente
	unsigned int cobro = Constantes::PRECIO_NAFTA * c.getCombustible();
	colaMensajesCaja.escribir(mpc); // TODO ver errores
	// Me bloqueo hasta que el asignador me asigne la caja a mi
	semaforoPeticionCaja.p();
	// Ya tengo la caja para mi, agrego el dinero
	caja_.agregarDinero(cobro);
	string msg = string("Le cobre al cliente y agregue a la caja $")
			+ Utilidades::int_to_string(cobro);
	getLogger().info(msg);
	// Aviso que termine de usar la caja
	semaforoUtilizandoCaja.v();
}

void Empleado::atender_cliente(Cliente& c) {
	// Tiempo que tarda atendiendo al cliente
	int tiempoAtencion = GeneradorRandom::obtenerNumero(
			Constantes::MINIMO_TIEMPO_ATENCION_EMPLEADO,
			Constantes::MAXIMO_TIEMPO_ATENCION_EMPLEADO);
	std::string msg = "Atendiendo al cliente, demorara aprox "
			+ Utilidades::int_to_string(tiempoAtencion) + " segundos";
	getLogger().info(msg);
	// Tiempo atendiendo al cliente
	sleep(tiempoAtencion);
}

bool Empleado::ocupar_surtidor(unsigned int pos) {
	this->getLogger().info(
			"Ocupando surtidor Nº " + Utilidades::int_to_string(pos + 1));
	return (this->surtidores_[pos].ocupar() == 1);
}

bool Empleado::es_surtidor_libre(unsigned int pos) {
	std::string msg = "Viendo si esta libre el surtidor "
			+ Utilidades::int_to_string(pos + 1);
	this->getLogger().info(msg);
	return (not this->surtidores_[pos].esta_ocupado());
}
bool Empleado::tomar_surtidor(const unsigned int pos) {
	this->semaforos_[pos].p(); //Toma semaforo del surtidor indicado para acceder a su estado
	int has_free = false;
	if (this->es_surtidor_libre(pos)) {
		std::string msg = "Surtidor libre: "
				+ Utilidades::int_to_string(pos + 1);
		this->getLogger().info(msg);
		try {
			has_free = this->ocupar_surtidor(pos);
		} catch (std::exception &e) {
			this->getLogger().error("ERROR al tratar de obtener surtidor");
		}
	}
	this->semaforos_[pos].v(); //libera semaforo del surtidor para que otro pueda ver su estado
	return has_free;
}
int Empleado::buscar_surtidor() {
	size_t size_surtidores = this->surtidores_.size();
	GeneradorRandom random;
	//primer acceso random a surtidores
	unsigned int nro_surtidor = random.obtenerNumero(0, size_surtidores - 1);
	bool has_not_free = true; //no hay surtidor libre
	do {
		//Si no pudo tomar el surtidor
		if (not this->tomar_surtidor(nro_surtidor)) {
			++nro_surtidor;
			std::string msg = "El surtidor "
					+ Utilidades::int_to_string(nro_surtidor) + "esta ocupado";
			this->getLogger().info(msg);
			if (nro_surtidor == size_surtidores) {
				nro_surtidor = 0;
			}
		}
		//pudo obtener el surtidor
		else {
			has_not_free = false;
		}
	} while (has_not_free);

	return nro_surtidor;
}

int Empleado::liberar_surtidor(const unsigned int pos) {
	std::string msg = "Liberando surtidor "
			+ Utilidades::int_to_string(pos + 1);
	this->getLogger().info(msg);
	return (this->surtidores_[pos].desocupar());
}

int Empleado::recibir_cliente(Cliente& c) {
	if (acceder_memoria_clientes_.p() < 0) {
		getLogger().error("Error en semop 'p' de Memoria Jefe-Empleado.");
		return -1;
	} else {
		try {
			c = memoria_clientes_.leer();
		} catch (std::exception& e) {
			getLogger().error("Error al intentar leer Cliente");
		}
		stringstream ss;
		ss << "Me fue asignado el cliente con ID: " << c.getId();
		getLogger().info(ss.str());
		return 0;
	}
}

void Empleado::finalizar_operacion() {
	if (estado_empleado_.p() < 0) {
		getLogger().error("Error en semop 'p' de Estado Empleado.");
	}
	empleados_ocupados_.escribir(false);
	if (estado_empleado_.v() < 0) {
		getLogger().error("Error en semop 'v' de Memoria Jefe-Empleado.");
	}
}

Semaforo& Empleado :: getSemaforoPeticionCaja() {
	return semaforoPeticionCaja;
}

int Empleado :: getPrioridadCaja() const {
	return prioridadCaja;
}

void Empleado::run() {
	int codigoSalida = 0; // Para ver si termino run por error o por la signal

	// event handler para la senial SIGINT (-2)
	SIGINT_Handler sigint_handler;
	// se registra el event handler declarado antes
	SignalHandler::getInstance()->registrarHandler(SIGINT, &sigint_handler);
	std::stringstream ss;
	ss << this->id_ << "Comenzando a trabajar" << std::endl;
	bool jefeFinalizado = false;

	while (sigint_handler.getGracefulQuit() == 0 && !jefeFinalizado) {
		Cliente c;
		if (this->recibir_cliente(c) == 0) {
			//Buscar un surtidor libre
			unsigned int indice_surtidor = this->buscar_surtidor();
			// Atiende al cliente
			this->atender_cliente(c);
			// Liberar surtidor
			this->liberar_surtidor(indice_surtidor);
			// Ya libero al cliente, guarda dinero en caja
			this->cobrar_y_guardar_en_caja(c);
			this->finalizar_operacion();
		} else {
			jefeFinalizado = true;
		}
	}

	// se recibio la senial SIGINT, el proceso termina
	if (codigoSalida == 0) {
		getLogger().warn("Se recibio la senial SIGINT, el proceso se termina.");
	}

	SignalHandler::destruir();
}

